Второй курс — самое время задуматься о будущей профессии и проанализировать существующие предложения. Дело тут даже не в том, чтобы найти интересную стажировку. В первую очередь сейчас стоит подумать о том, в какую сторону развиваться дальше. Например, если вы хотите работать в какой-либо конкретной профессии, то наверняка стоит развивать какие-то определенные навыки, и даже выбрать подходящую кафедру. Анализ существующих вакансий поможет определить, какие навыки вам нужны.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
sns.set(style='whitegrid', font_scale=1.3, palette='Set2')
%matplotlib inline
import requests
from time import sleep
import json
Исследуем профессию Data Scientist. Найдите как можно больше вакансий по этой профессии в Москве. Учтите, что имеет смысл искать также по другим ключевым словам, например аналитик данных.
Сделаем тестовый запрос, чтобы понять основные параметры, по которым будем обрабатывать страницы:
URL = 'https://api.hh.ru/vacancies'
ds_params = dict(text="\"Data Scientist\" OR"
"\"Data Analysis\" OR"
"\"Data Analyst\" OR"
"\"Data Science\" OR"
"\"ML\" OR"
"\"Data Engineer\" OR"
"\"Big Data\" OR"
"\"Machine learning\" OR"
"\"Наука о данных\" OR"
"\"Анализ Данных\" OR"
"\"Инженер Данных\" OR"
"\"Kaggle\" OR"
"\"Аналитик Данных\" OR"
"\"Машинное обучение\"", area=1, page={}, per_page=100)
ds_req = requests.get(URL, ds_params)
ds_data = json.loads(ds_req.content.decode())
Согласно документации по API пользователю не может быть выдано более $2000$ вакансий. Таким образом, для дальнейшего анализа нам остаётся в лучшем случае имеено такое число.
Далее пройдёмся по всем страницам и соберём полный список вакансий:
ds_pages = []
for n in range(0, ds_data['pages']):
ds_params['page'] = n
req = requests.get(URL, ds_params)
data = json.loads(req.content.decode())
ds_pages.append(pd.json_normalize(data['items']))
sleep(0.5)
Соберём таблицы вместе и выведем, чтобы представлять, как выглядят сырые данные:
ds_pages_df = pd.concat(ds_pages)
ds_pages_df.head()
| id | premium | name | department | has_test | response_letter_required | salary | response_url | sort_point_distance | published_at | ... | address.metro | salary.from | salary.to | salary.currency | salary.gross | department.id | department.name | employer.logo_urls | insider_interview.id | insider_interview.url | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 52324250 | False | Middle Data Scientist (Big Data) | NaN | False | False | NaN | None | None | 2022-03-17T14:31:11+0300 | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 1 | 51703352 | False | Аналитик данных (Data science) | NaN | False | False | NaN | None | None | 2022-03-17T12:39:28+0300 | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 2 | 52234562 | False | Data Engineer / Big data (Удаленно) | NaN | False | False | NaN | None | None | 2022-03-17T15:02:03+0300 | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 3 | 53816693 | False | Data Scientist | NaN | False | False | NaN | None | None | 2022-03-17T18:46:11+0300 | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 4 | 52480091 | False | Data analyst / Аналитик данных | NaN | False | False | NaN | None | None | 2022-03-17T13:45:04+0300 | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
5 rows × 66 columns
В полученную выборку некоторые вакансии могли попасть несколько раз. Удалите дубликаты.
Воспользуемся тем, что у одинаковых объявлений одинаковые id:
ds_pages_df.reset_index(inplace=True)
ds_pages_df.drop_duplicates(subset=['id'], inplace=True)
ds_pages_df.drop('index', axis=1, inplace=True)
Загрузите подробное описание каждой вакансии и создайте удобную таблицу данных.
ds_vacancies_info= []
ds_key_skills = []
ds_coord_lat = []
ds_coord_lng = []
ds_exp = []
ds_employer_name = []
for s in range(0, ds_pages_df.shape[0]):
vacancy = ds_pages_df['id'].iloc[s]
vacancy_url = f'https://api.hh.ru/vacancies/{vacancy}'
req = requests.get(vacancy_url)
vacancy_info = json.loads(req.content.decode())
# на случай, если сервер перестанет отдавать ответы
if 'errors' in vacancy_info:
print("уходи, дудосер ٩(ఠ益ఠ)۶")
break
ds_vacancies_info.append(vacancy_info['description'])
ds_key_skills.append([skill['name'] for skill in vacancy_info['key_skills']])
# обрабатываем айди-имя работодателя
if vacancy_info['employer'] is not None \
and 'name' in vacancy_info['employer']:
emp_name = vacancy_info['employer']['name']
ds_employer_name.append(emp_name)
else:
ds_employer_name.append(None)
# обрабатываем опыт работы
if vacancy_info['experience'] is not None \
and 'id' in vacancy_info['experience']:
ds_exp.append(vacancy_info['experience']['id'])
else:
ds_exp.append(None)
# обрабатываем координату работы
if vacancy_info['address'] is not None \
and 'lat' in vacancy_info['address'] \
and 'lng' in vacancy_info['address'] :
ds_coord_lat.append(vacancy_info['address']['lat'])
ds_coord_lng.append(vacancy_info['address']['lng'])
sleep(0.5)
Оставим важные для анализа поля. Для меня как для человека, который недавно искал работу, важна зарплата и описание вакансии вкупе с ключевыми навыками. Оставлю так же имя работодателя и требуемый опыт работы.
target_fields = ['id', 'name', 'salary.from', 'salary.to', 'salary.currency', 'salary.gross']
# TODO добавить адрес и работодателя потом
target_ds = ds_pages_df.loc[:, target_fields]
# добавляем необходимые для анализа поля
target_ds['vacancy_info'] = ds_vacancies_info
target_ds['key_skills'] = ds_key_skills
target_ds['experience'] = ds_exp
target_ds['employer_name'] = ds_employer_name
Полученную таблицу необходимо сохранить в формате xlsx и отправить боту вместе с решением.
target_ds.to_excel('ds_vacancy_info.xlsx')
Вопрос 1. Сколько сейчас доступно вакансий по вашему запросу?
data['found']
5324
Но, в связи с ограничениями API, к анализу доступно меньше, а именно, $2000$.
Вопрос 2. Какие навыки чаще всего встречаются в вакансиях по данной специальности?
Для этого найдите соответствующее поле в данных, проанализируйте его и составьте список навыков и количество упоминаний каждого. Визуализируйте полученную информацию по топ-15 навыков.
ds_skills_useability = {}
# идём по каждой вакансии, считаем упоминаемость навыков
for i in range(0, len(ds_key_skills)):
for skill in ds_key_skills[i][:]:
normalized_skill = skill.lower()
if normalized_skill in ds_skills_useability:
ds_skills_useability[normalized_skill] += 1
else:
ds_skills_useability[normalized_skill] = 1
Заметим, что навыки вроде 'Анализ данных' или подвиды SQL представляют для основной картины по навыкам несущественную роль. Объединим признаки по группам:
sk_tmp = ds_skills_useability.copy()
sk_tmp['sql'] += sk_tmp['ms sql'] + sk_tmp['postgresql']
sk_tmp.pop('ms sql')
sk_tmp.pop('postgresql')
sk_tmp.pop('анализ данных')
sk_tmp.pop('data analysis')
# формируем топ
sorted_useability = pd.DataFrame(sorted(sk_tmp.items(), key=lambda item: item[1], reverse=True),
columns=['Навык', 'Упоминаемость'])
plt.figure(figsize=(16, 6))
sns.set(style='darkgrid')
plt.scatter(data=sorted_useability[0:15],
x='Навык',
y='Упоминаемость',
marker='.',
s=200,
color='crimson')
plt.xlabel('Навык', fontsize=15)
plt.ylabel('Упоминаемость в резюме', fontsize=15)
plt.xticks(rotation=15, ha='right')
plt.title('Анализ топ-15 навыков из резюме Data Science', fontsize=15)
Text(0.5, 1.0, 'Анализ топ-15 навыков из резюме Data Science')
Таким образом, популярны инструменты работы с базами данных и инструменты визуализации.
Вопрос 3. Какую зарплату готовы платить работодатели? Соберите некоторым образом статистику и постройте гистограмму.
При работе с данными о заработной плате обратите внимание на валюту и gross/net.
Рассмотрим состояние данных о зарплате и постараемся привести их к более единому виду.
target_ds['salary.from'].count()
508
not_null_gross = target_ds['salary.gross'].count()
not_null_gross
588
target_ds['salary.to'].count()
390
Таким образом, заметим, что среди вакансий меньше чем в $30\%$ указана одна из двух границ вилок. Оставим только необходимые для анализа поля:
salary_fields = ['salary.from', 'salary.to', 'salary.currency', 'salary.gross']
salary_info = target_ds.loc[:, salary_fields]
salary_info.dropna(inplace=True)
Рассмотрим число предложений с gross и net соответственно:
cnt_gross = target_ds['salary.gross'].sum()
print(cnt_gross, not_null_gross - cnt_gross)
192 396
Таким образом, работодатели чаще предпочитают указывать net в заработных платах.
В связи с тем, что европейский рынок рынок предлагает традиционно более высокую вилку, чем российский, расмотрим отдельно зарплаты в рублях и зарплаты в долларах и евро. Посмотрим график по зарплате в рублях:
plt.figure(figsize=(16, 8))
sns.set(style='darkgrid')
rub_salary = salary_info[salary_info['salary.currency'] == 'RUR']
fig = sns.scatterplot(
data=rub_salary,
x='salary.from',
y='salary.to',
s=70,
linewidth=0,
hue='salary.gross',
palette='viridis',
alpha=0.6,
)
plt.xlim((0, 360000))
plt.ylim((0, 800000))
plt.ticklabel_format(style='plain')
plt.xlabel('Нижняя граница вилки', fontsize=15)
plt.ylabel('Верхняя граница вилки', fontsize=15)
plt.title('Распределение зарплат в вакансиях, рубли', fontsize=15)
plt.legend(title='gross')
<matplotlib.legend.Legend at 0x7f2bdb99a760>
Таким образом мы можем видеть, что нет явного превосходства одной группы зарплат над другой. Почти на каждое gross предложение есть похожее по вилке net. Тем временем, gross зарплаты не выбиваются в отдельную, более высокую по количеству денег категорию, таким образом, сэкономленные налоги не оказываются в кармане сотрудника.
Построим гистограмму распределения зарплат. В качестве значения для гистограммы возьмём среднее арифметическое между двумя границами:
plt.figure(figsize=(16, 8))
sns.set(style='whitegrid')
fig = sns.histplot(
data=rub_salary,
x=0.5 * (rub_salary['salary.from'] + rub_salary['salary.to']),
hue='salary.gross',
linewidth=0,
bins=50,
palette='viridis',
alpha=0.6,
)
xmin = 0
xmax = 400000
plt.xlim((xmin, xmax))
fig.set_xticks(np.arange(xmin, xmax, 50000))
plt.xlabel('Зарплата', fontsize=15)
plt.ylabel('Число вакансий', fontsize=15)
plt.title('Распределение размера зарплаты, рубли', fontsize=15)
Text(0.5, 1.0, 'Распределение размера зарплаты, рубли')
Мы видим, что основная масса работодателей предпочитает нанимать сотрудников за $50-250$ тысяч рублей. Данная статистика соответствует информации по зарплатам, которую я смогла найти от реальных сотрудников на сайте https://www.glassdoor.com/. Заметим, что тенденция для зарплаты с налогами и без одинаковая, что ещё раз подтверждает вывод, сделанный по предыдущему графику.
Вычислим медианные границы:
rub_salary[rub_salary['salary.gross'] == False].median()
salary.from 80000.0 salary.to 130000.0 salary.gross 0.0 dtype: float64
rub_salary[rub_salary['salary.gross'] == True].median()
salary.from 100000.0 salary.to 142500.0 salary.gross 1.0 dtype: float64
Медианная зарплата:
np.median(0.5 * (rub_salary['salary.from'] + rub_salary['salary.to']))
110000.0
Повторим те же действия для зарплат в иностранной валюте. Для удобства приведём все зарплаты к доллару:
usd_eur_salary = salary_info.loc[salary_info['salary.currency'] != 'RUR']
# взято по официальному курсу
usd_div_eur = 0.91441
usd_eur_salary.loc[usd_eur_salary['salary.currency'] == 'EUR', ['salary.to', 'salary.from']] *= usd_div_eur
usd_eur_salary.loc[usd_eur_salary['salary.currency'] == 'EUR', 'salary.currency'] = 'USD'
Посмотрим, как работодатели, которые платят в иностранной валюте, предпочитают указывать: net или gross.
not_null_gross = usd_eur_salary['salary.gross'].count()
cnt_gross = usd_eur_salary['salary.gross'].sum()
print(cnt_gross, not_null_gross - cnt_gross)
11 18
Видим, что процент gross выше, чем в рублёвом случае. В традициях иностранных рынков указывать зарплату без налогов, что связано со сложной (прогрессивной) системой налогообложения и различными налоговыми законодательствах в географически близких странах европы.
plt.figure(figsize=(16, 8))
sns.set(style='darkgrid')
fig = sns.scatterplot(
data=usd_eur_salary,
x='salary.from',
y='salary.to',
s=70,
linewidth=0,
hue='salary.gross',
palette='viridis',
alpha=0.6,
)
plt.ticklabel_format(style='plain')
plt.xlabel('Нижняя граница вилки', fontsize=15)
plt.ylabel('Верхняя граница вилки', fontsize=15)
plt.title('Распределение зарплат в вакансиях, доллары', fontsize=15)
plt.legend(title='gross')
plt.show()
Здесь мы наблюдаем, что вакансии gross находятся в среднем правее и ниже, чем группа net. Это означает, что в среднем по gross зарплата выше, но ширина вилки -- ниже, в отличие от рублёвых зарплат.
Поскольку мы имеем дело с маленькой выборкой по долларовым ЗП, гистограмма не является информативной.
usd_eur_salary[usd_eur_salary['salary.gross'] == False].median()
salary.from 2500.0 salary.to 3750.0 salary.gross 0.0 dtype: float64
usd_eur_salary[usd_eur_salary['salary.gross'] == True].median()
salary.from 3657.64 salary.to 7000.00 salary.gross 1.00 dtype: float64
Медианная зарплата:
np.median(0.5 * (usd_eur_salary['salary.from'] + usd_eur_salary['salary.to']))
3657.64
Таким образом, разброс зарплат сильнее, чем у рублёвых. Но строгие выводы делать сложно, так как в выборке представлено небольшое число USD вакансий. По текущему курсу медианная зарплата $3.5 - 4.0$ раза выше, чем рублёвая.
Вывод:
Проведите аналогичный анализ для наиболее привлекательной для вас профессии в любом регионе. Если это Data Scientist, то для анализа выберите другую.
Рассмотрю профессию менеджера в городе Санкт-Петербург. Повторим аналогичные действия. Рассматриваю одно ключевое слово, так как менеджера иначе как менеджером назвать сложно :)
URL = 'https://api.hh.ru/vacancies'
params = dict(text="\"Менеджер\"", area=1, page={}, per_page=100)
req = requests.get(URL, params)
m_data = json.loads(req.content.decode())
m_pages = []
for n in range(0, m_data['pages']):
params['page'] = n
req = requests.get(URL, params)
m_data = json.loads(req.content.decode())
m_pages.append(pd.json_normalize(m_data['items']))
sleep(0.5)
m_pages_df = pd.concat(m_pages)
m_pages_df.reset_index(inplace=True)
m_pages_df.drop_duplicates(subset=['id'], inplace=True)
m_pages_df.drop('index', axis=1, inplace=True)
Вопрос 1. Сколько сейчас доступно вакансий по вашему запросу?
m_data['found']
37507
Вопрос 2. Какие навыки чаще всего встречаются в вакансиях по данной специальности?
Для этого найдите соответствующее поле в данных, проанализируйте его и составьте список навыков и количество упоминаний каждого. Визуализируйте полученную информацию по топ-15 навыков.
from tqdm.notebook import tqdm
bar = tqdm(range(0, m_pages_df.shape[0]))
0%| | 0/2000 [00:00<?, ?it/s]
m_vacancies_info= []
m_key_skills = []
m_exp = []
m_employer_name = []
for s in range(0, m_pages_df.shape[0]):
vacancy = m_pages_df['id'].iloc[s]
vacancy_url = f'https://api.hh.ru/vacancies/{vacancy}'
req = requests.get(vacancy_url)
vacancy_info = json.loads(req.content.decode())
# на случай, если сервер перестанет отдавать ответы
if 'errors' in vacancy_info:
print("уходи, дудосер ٩(ఠ益ఠ)۶")
break
m_vacancies_info.append(vacancy_info['description'])
m_key_skills.append([skill['name'] for skill in vacancy_info['key_skills']])
# обрабатываем айди-имя работодателя
if vacancy_info['employer'] is not None \
and 'name' in vacancy_info['employer']:
emp_name = vacancy_info['employer']['name']
m_employer_name.append(emp_name)
else:
m_employer_name.append(None)
# обрабатываем опыт работы
if vacancy_info['experience'] is not None \
and 'id' in vacancy_info['experience']:
m_exp.append(vacancy_info['experience']['id'])
else:
m_exp.append(None)
bar.update()
sleep(0.5)
target_fields = ['id', 'name', 'salary.from', 'salary.to', 'salary.currency', 'salary.gross']
target_m = m_pages_df.loc[:, target_fields]
target_m['vacancy_info'] = m_vacancies_info
target_m['key_skills'] = m_key_skills
Сохраняем в таблицу:
target_m.to_excel('manager_df.xlsx')
Рассмотрим навыки:
m_skills_useability = {}
# идём по каждой вакансии, считаем упоминаемость навыков
for i in range(0, len(m_key_skills)):
for skill in m_key_skills[i][:]:
normalized_skill = skill.lower()
if normalized_skill in m_skills_useability:
m_skills_useability[normalized_skill] += 1
else:
m_skills_useability[normalized_skill] = 1
sk_tmp = m_skills_useability.copy()
dict_sorted = sorted(sk_tmp.items(), key=lambda item: item[1], reverse=True)
sorted_useability = pd.DataFrame(dict_sorted, columns=['Навык', 'Упоминаемость'])
plt.figure(figsize=(16, 4))
sns.set(style='darkgrid')
plt.scatter(data=sorted_useability[0:15],
x='Навык',
y='Упоминаемость',
marker='.',
s=200,
color='crimson')
plt.xlabel('Навык', fontsize=15)
plt.ylabel('Упоминаемость в резюме', fontsize=15)
plt.xticks(rotation=15, ha='right')
plt.title('Анализ топ-15 навыков из резюме менеджеров', fontsize=15)
Text(0.5, 1.0, 'Анализ топ-15 навыков из резюме менеджеров')
Вопрос 3. Какую зарплату готовы платить работодатели? Соберите некоторым образом статистику и постройте гистограмму. Оставляем необходимые поля:
salary_fields = ['salary.from', 'salary.to', 'salary.currency', 'salary.gross']
salary_info = target_m.loc[:, salary_fields]
salary_info.dropna(inplace=True)
Рассмотрим число предложений с gross и net соответственно:
not_null_gross = target_m['salary.gross'].count()
cnt_gross = target_m['salary.gross'].sum()
print(cnt_gross, not_null_gross - cnt_gross)
381 1480
Таким образом, и в случае вакансий с менеджментом, работодатели предпочитают указывать зарплату net.
Посмотрим график по зарплате в рублях:
plt.figure(figsize=(16, 8))
sns.set(style='darkgrid')
rub_salary = salary_info[salary_info['salary.currency'] == 'RUR']
fig = sns.scatterplot(
data=rub_salary,
x='salary.from',
y='salary.to',
s=70,
linewidth=0,
hue='salary.gross',
palette='viridis',
alpha=0.6,
)
plt.xlim((0, 260000))
plt.ylim((0, 800000))
plt.ticklabel_format(style='plain')
plt.xlabel('Нижняя граница вилки', fontsize=15)
plt.ylabel('Верхняя граница вилки', fontsize=15)
plt.title('Распределение зарплат в вакансиях, рубли', fontsize=15)
plt.legend(title='gross')
<matplotlib.legend.Legend at 0x7f2b97578310>
Таким образом, в отличие от зарплаты аналитика, зарплаты менеджеров указывают с бОльшей шириной вилки.
plt.figure(figsize=(16, 8))
sns.set(style='whitegrid')
fig = sns.histplot(
data=rub_salary,
x=0.5 * (rub_salary['salary.from'] + rub_salary['salary.to']),
hue='salary.gross',
linewidth=0,
palette='viridis',
alpha=0.6,
)
xmin = 0
xmax = 260000
plt.xlim((xmin, xmax))
fig.set_xticks(np.arange(xmin, xmax, 25000))
plt.xlabel('Зарплата', fontsize=15)
plt.ylabel('Число вакансий', fontsize=15)
plt.title('Распределение размера зарплаты, рубли', fontsize=15)
Text(0.5, 1.0, 'Распределение размера зарплаты, рубли')
Интересно, что распределение зарплат в данном случае хорошо описывается нормальным распределением со смещённым центром. Возможно, это связано с тем, что мы имеем бОльшую выборку.
Вычислим медианные границы:
rub_salary[rub_salary['salary.gross'] == False].median()
salary.from 70000.0 salary.to 137000.0 salary.gross 0.0 dtype: float64
rub_salary[rub_salary['salary.gross'] == True].median()
salary.from 60700.0 salary.to 125000.0 salary.gross 1.0 dtype: float64
np.median(0.5 * (rub_salary['salary.from'] + rub_salary['salary.to']))
100000.0
Заметим, что медианная зп менеджера отличается всего лишь на 10 тысяч рублей от медианной зарплаты аналитика.
Повторим те же действия для зарплат в долларах:
usd_eur_salary = salary_info.loc[salary_info['salary.currency'] != 'RUR']
# взято по официальному курсу
usd_div_eur = 0.91441
usd_eur_salary.loc[usd_eur_salary['salary.currency'] == 'EUR', ['salary.to', 'salary.from']] *= usd_div_eur
usd_eur_salary.loc[usd_eur_salary['salary.currency'] == 'EUR', 'salary.currency'] = 'USD'
not_null_gross = usd_eur_salary['salary.gross'].count()
cnt_gross = usd_eur_salary['salary.gross'].sum()
print(cnt_gross, not_null_gross - cnt_gross)
0 8
Поскольку указано всего лишь 8 зарплат, построим только scatterplot. Выборка слишком мала, чтобы делать по ней какие-то выводы.
usd_eur_salary[usd_eur_salary['salary.gross'] == False].median()
salary.from 1400.0 salary.to 3000.0 salary.gross 0.0 dtype: float64
np.median(0.5 * (usd_eur_salary['salary.from'] + usd_eur_salary['salary.to']))
1975.0
По тем данным, что у нас есть, зарплата в долларах для менеджера не даёт такого большого денежного преимущества, как оплата в долларах для аналитика.
Вывод:
Для одной из рассмотренных ранее профессий ответьте на следующие вопросы:
Не забудьте про визуализацию, для этого используйте библиотеку plotly. В частности, постройте распределений вакансий на карте.
Для построения последних двух графиков вам могут понадобиться внешние данные.
В качестве анализа выберем Data Science
plt.figure(figsize=(10, 6))
sns.set(style='whitegrid')
fig = sns.countplot(x=ds_exp, color='crimson', alpha=0.8)
plt.legend()
fig.set_xticklabels(('От 1 до 3 лет', 'От 3 до 6 лет', 'Без опыта', 'Более 6 лет'))
plt.xlabel('Число резюме', fontsize=15)
plt.ylabel('Опыт работы', fontsize=15)
plt.title('Требуемый опыт работы для Data Science', fontsize=15)
Таким образом, на рынке более востребованы Middle+, Middle и Junior специалисты.
company = {}
for name in ds_employer_name:
if name in company:
company[name] += 1
else:
company[name] = 1
sorted_top_empl = pd.DataFrame(sorted(company.items(), key=lambda item: item[1], reverse=True),
columns=['Работодатель', 'Число вакансий'])
# переименую, чтобы занимали меньше места
sorted_top_empl['Работодатель'][4] = 'Центробанк'
sorted_top_empl['Работодатель'][7] = 'Минцифры'
plt.figure(figsize=(16, 8))
sns.set(style='darkgrid')
plt.scatter(data=sorted_top_empl[0:15],
x='Работодатель',
y='Число вакансий',
marker='.',
s=500,
color='crimson')
plt.xlabel('Работодатель', fontsize=15)
plt.ylabel('Число вакансий', fontsize=15)
plt.xticks(rotation=15, ha='right')
plt.title('Топ-15 нанимателей по числу вакансий', fontsize=15)
Text(0.5, 1.0, 'Топ-15 нанимателей по числу вакансий')
Таким образом, чаще всего аналитиков ищут IT-компании и банки.
import plotly.express as px
fig = px.scatter_mapbox(lat=ds_pages_df['address.lat'],
lon=ds_pages_df['address.lng'],
color_discrete_sequence=["purple"],
zoom=11,
width=1000 ,
height=700,
center = dict(
lat = 55.753995,
lon = 37.614069,
),
mapbox_style="open-street-map")
fig.show()
Далее объединим по районам, используя названия станций метро:
target_col = ['address.metro.station_id', 'address.metro.lat', 'address.metro.lng', 'address.metro.station_name']
stages = ds_pages_df.groupby(target_col).count().reset_index()
px.scatter_mapbox(
lat=stages['address.metro.lat'],
lon=stages['address.metro.lng'],
zoom=10,
color_discrete_sequence=["purple"],
width=1000 ,
height=700,
center = dict(
lat = 55.753995,
lon = 37.614069,
),
mapbox_style="open-street-map",
size=stages['id'],
hover_name=stages['address.metro.station_name']
)
Какие выводы можно сделать из построенных графиков?
Вывод: Таким образом, офисы нанимателей располагаются ближе к центру Москвы. Внутри третьего транспортного кольца располагается подавляющее большинство вакансий, вне него не такое большое количество, что можно заметить по второму графику. Особая концентрация вакансия находится в районе Делового Центра в Москва-Сити, что связано с большим расположением офисов различных компаний и в Замоскорецком районе, где рядом с Павелецким вокзалом располагается крупный бизнес-центр.
Популярными для размещения офисов нанимателей стали станции метро Бауманская, Белорусская, Павелецкая, Технопарк, Аэропорт, Динамо.